#include "main.h"

// Global begin

Pipe<char, 32> buffer;
TaskList<32> tasks;

#define DMA_BUF_SIZE 32
uint8_t dma_buf[DMA_BUF_SIZE] = "[Task]: Here's task<3> by DMA.\n";

// Global end

static void RCC_Config()
{
	RCC_t::AHB1::enable(RCC_AHB1Periph_GPIOA,
	                    RCC_AHB1Periph_GPIOC,
	                    RCC_AHB1Periph_GPIOH,
	                    RCC_AHB1Periph_DMA2);

	RCC_t::APB2::enable(RCC_APB2Periph_USART1,
	                    RCC_APB2Periph_SYSCFG);
}

static void NVIC_Config()
{
	NVIC_t::group_config(NVIC_PriorityGroup_2);

	// Key1
	NVIC_t::init(EXTI0_IRQn, 1, 1, ENABLE);

	// Key2
	NVIC_t::init(EXTI15_10_IRQn, 1, 1, ENABLE);

	// USART1
	NVIC_t::init(USART1_IRQn, 1, 1, ENABLE);

	// SWI
	NVIC_t::init(EXTI2_IRQn, 1, 1, ENABLE);
}

static void SYSCFG_Config()
{
	// Key1
	SYSCFG_t::exti_line_config(EXTI_PortSourceGPIOA, EXTI_PinSource0);

	// Key2
	SYSCFG_t::exti_line_config(EXTI_PortSourceGPIOC, EXTI_PinSource13);
}

static void EXTI_Config()
{
	// Key1
	EXTI_t::init(EXTI_Line0, EXTI_Mode_Interrupt, EXTI_Trigger_Rising, ENABLE);

	// Key2
	EXTI_t::init(EXTI_Line13, EXTI_Mode_Interrupt, EXTI_Trigger_Rising, ENABLE);

	// SWI
	EXTI_t::init(EXTI_Line2, EXTI_Mode_Interrupt, EXTI_Trigger_Rising, ENABLE);
}

static void USART_Config()
{
	convert(USART1)
	        .init(115200, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No)// 115200-8-1-N
	        .rx_config(GPIOA, GPIO_t::get_pin_src(10), GPIO_AF_USART1)           // RX -> PA10
	        .tx_config(GPIOA, GPIO_t::get_pin_src(9), GPIO_AF_USART1)            // TX -> PA9
	        .it_enable(USART_IT_RXNE)                                            // Interrupt when RXNE -> USART1_IRQHandler
	        .enable();
}

static void DMA_Config()
{
	auto& dma2_s7 = convert(DMA2_Stream7).deinit();

	/* 确保 DMA 数据流复位完成 */
	while (dma2_s7.get_cmd_status() != DISABLE) {
	}

	/*配置 DMA2 的数据流 7*/
	dma2_s7.init({
	        .DMA_Channel            = DMA_Channel_4,               /* USART1-TX 对应 DMA2，通道4，数据流7 */
	        .DMA_PeripheralBaseAddr = (USART1_BASE + 0x04),        /* 设置DMA源：USART1 DR地址 */
	        .DMA_Memory0BaseAddr    = (uint32_t) dma_buf,          /* 内存地址(要传输的变量的指针) */
	        .DMA_DIR                = DMA_DIR_MemoryToPeripheral,  /* 方向：从内存到外设 */
	        .DMA_BufferSize         = DMA_BUF_SIZE,                /* 传输大小 DMA_BufferSize=DMA_BUF_SIZE */
	        .DMA_PeripheralInc      = DMA_PeripheralInc_Disable,   /* 外设地址不增 */
	        .DMA_MemoryInc          = DMA_MemoryInc_Enable,        /* 内存地址自增 */
	        .DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte, /* 外设数据单位 */
	        .DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte,     /* 内存数据单位 8bit */
	        .DMA_Mode               = DMA_Mode_Normal,             /* DMA模式：单次 */
	        .DMA_Priority           = DMA_Priority_Medium,         /* 优先级：中 */
	        .DMA_FIFOMode           = DMA_FIFOMode_Disable,        /* 禁用 FIFO */
	        .DMA_FIFOThreshold      = DMA_FIFOThreshold_Full,      /* 禁用 FIFO */
	        .DMA_MemoryBurst        = DMA_MemoryBurst_Single,      /* 存储器突发传输 16个节拍 */
	        .DMA_PeripheralBurst    = DMA_PeripheralBurst_Single,  /* 外设突发传输 1个节拍 */
	});

	/* 使能 DMA */
	dma2_s7.enable();

	/* 等待 DMA 数据流有效 */
	while (dma2_s7.get_cmd_status() != ENABLE) {
	}
}

static void bsp_init()
{
	RCC_Config();
	NVIC_Config();
	SYSCFG_Config();
	EXTI_Config();
	USART_Config();
	DMA_Config();
	SysTick_t::config();

	debug_info("[System]: Init completed.\n");
}

int8_t task0(void* args)
{
	debug_info("[Task]: Here's task<0>.\n");
	EXTI_t::generate_swi(EXTI_Line2);
	return 0;
}

int8_t task1(void* args)
{
	debug_info("[Task]: Here's task<1>.\n");

	constexpr GPIO_t::Pin_t pins[] = {
	        GPIO_t::get_pin(10),        // red
	        GPIO_t::get_pin(11),        // green
	        GPIO_t::get_pin(12),        // blue
	        GPIO_t::get_pin(10, 11),    // yellow
	        GPIO_t::get_pin(10, 12),    // purple
	        GPIO_t::get_pin(11, 12),    // cyan
	        GPIO_t::get_pin(10, 11, 12),// white
	};

	auto to_index = [](char c) constexpr {
		return (c < '0' || c > '6') ? 6 : c - 48;
	};

	while (!buffer.empty()) {
		char ch = buffer.serve();
		debug_info("[App]: led[%c] blinks!\n", ch);
		LED_t {GPIOH, pins[to_index(ch)]}.blink(1, 300);
	}

	return 0;
}

int8_t task2(void* args)
{
	debug_info("[Task]: Here's task<2>.\n");
	debug_info("[Buffer = %d]", buffer.size());
	debug_info(" -> [");

	buffer.iter([](const auto& x) {
		debug_info("%c, ", x);
	});

	debug_info("]\n");
	return 0;
}

int8_t task3(void* args)
{
	/* USART1 向 DMA 发出 TX 请求，传输开始 */
	convert(USART1).dma_tx_enable();

	return 0;
}

static void start()
{
	tasks.push(Task {task0, 15}, Task {task3, 15});

	while (true) {
		while (!tasks.empty()) {
			auto&& tmp = tasks.serve();
			int8_t ret = tmp(NULL);
		}
	}
}

// Entry
int main(void)
{
	bsp_init();
	start();
}
